unit oCellularMotion;

interface

uses oLevel, Classes, oCoordinates, oPolygon, oPedestrian, oCellularWorld, oPedActor,
     types, oSink, oEntityTypeConstants, oCell, oPhysicalEntity, Windows, Forms, oLogicalEntity,
     Dialogs, uOSPList, SysUtils, StdCtrls, Contnrs, Generics.Collections, oEntity, oPathsManager,
     oPathFinding;


///toto je to co by malo byt v unite oBA vo funkcii BASim
function Step(paWorld : TCellularWorld; paPedestrian:TPedestrian):boolean;

///toto sa v nej vyuziva
function IsRelevantCell(paLevel: TCellularWorld; paX,paY: integer):boolean; // ci je bunka priechodna
function GetGain(paLevel : TCellularWorld; paDest : tLogicalEntity; paActPos : tPoint; paDX,paDY: integer):integer;// vrati vyhodnost posunutia o jedno policko voci cielu, vracia : 2,1,0,-1,-2
function GetCountOfPedestrians(paWorld : TCellularWorld; paX,paY:integer; paDistance:integer):integer; // v cellsvete paWorld spocitaj pocet chodcov v okoli x,y vratane, do vzdialenosti paDistance
function GetCountOfBarrierCells(paWorld : TCellularWorld; paX,paY:integer; paDistance:integer):integer; // v cellsvete paWorld spocitaj pocet nepriechodnych buniek v okoli x,y vratane, do vzdialenosti paDistance
function IsDestinationReached(paDestEntity : tLogicalEntity; paCell : tCell):boolean;overload;
function IsDestinationReached(paActor : tPedActor):boolean;overload;


///toto su iba pre mna metody
procedure InsertBarrier(paWorld : tLevel; paID : integer ; paLU_x, paLU_y, paRU_x, paRU_y, paLD_x, paLD_y, paRD_x, paRD_y:Real);
procedure InsertPedestrians(paDiagonalMove : boolean; paCrowdTightning, paBarrierTightning : real;  paGainTightning : integer; paWorld : tLevel; paFarba : integer; paPosition : tCoordinates; paGoal : tSink; paMatrix : fMatrix);
procedure PrepareWorld(paWorld : tLevel; paState : word);
procedure MoveAll;


var Svet : tLevel;
    Stop : boolean;

implementation

Uses oFormDocasneVykreslovanieNaGrid;


procedure InsertPedestrians(paDiagonalMove : boolean; paCrowdTightning, paBarrierTightning : real; paGainTightning : integer; paWorld : tLevel; paFarba : integer; paPosition : tCoordinates; paGoal : tSink; paMatrix : fMatrix);
  var pomPede : TPedestrian;
      i, j : integer;
begin
  for i := -3 to 3 do
    for j := -3 to 3 do
      if (paPosition.X  + (10 * i) > 0) and (paPosition.X  + (10 * i) < paWorld.SizeX) and (paPosition.Y  + (10 * j) > 0) and (paPosition.Y  + (10 * j) < paWorld.SizeY) then begin
      pomPede := TPedestrian .Create;
      pomPede.Target := paGoal;
      pomPede.Matrix := paMatrix;
      pomPede.Position.X := paPosition.X  + (10 * i);
      pomPede.Position.Y := paPosition.Y + (10 * j);
      pomPede.Typ := paFarba;
      pomPede.Destination := pomPede.Target.Position;
      pomPede.CrowdTightning := paCrowdTightning;
      pomPede.BarrierTightning := paBarrierTightning;
      pomPede.GainTightning := paGainTightning;
      pomPede.DiagonalMoving := paDiagonalMove;
      paWorld.insertPedestrian(pomPede);
    end;
end;

procedure MoveAll;
  var pomPede : TPedestrian;
      i : integer;
begin
  i := 1;
  Stop := False;
  while (NOT Stop) and (Svet.AllPedestrians.Count <> 0) do begin
    pomPede := Svet.AllPedestrians.Items[i - 1];
    Sleep(Docasny.TrackBar1.Position * 5);
    If Step(Svet.CellWorld,pomPede) then begin
      Svet.deletePedestrian(pomPede,tSink(pomPede.Target));
      Docasny.Grid.Repaint;
    end;
    inc(i);
    if i > Svet.AllPedestrians.Count then i := 1;
    Application.ProcessMessages;
  end;
end;

function IsDestinationReached(paDestEntity : TLogicalEntity; paCell : tCell):boolean;
begin
  if paCell.LogEntities.IndexOf(paDestEntity) <> -1 then
    IsDestinationReached := true
  else
    IsDestinationReached := false;
end;

function IsDestinationReached(paActor : tPedActor):boolean;
begin
  result := TLogicalEntity(paActor.Target).Shape.IsInner(TLogicalEntity(paActor.Target).Shape.Rect, paActor.Pedestrian.Position);
end;


function Step(paWorld : TCellularWorld; paPedestrian:TPedestrian):boolean;
  var x,y : integer;
      gain, resGain : integer;
      pedCount, barrierCount : integer;
      resultX, resultY : integer;
      profit,best : real;
      destPosSpoj : cPoint;
      destPos : tCoordinates;
begin
  result := false;
  best := - MaxInt;
  with paPedestrian do begin
    //kukam okolite bunky
    for x := -1 to 1 do begin
      for y := -1 to 1 do begin

        if (DiagonalMoving) or (abs(x) <> abs(y)) then begin // ci sa hybem po diagonalach
          if IsIndexOK(paWorld, paPedestrian.CellIndex.X + x, paPedestrian.CellIndex.Y + y) then // ak sa nepytam mimo sveta
          if IsRelevantCell(paWorld, paPedestrian.CellIndex.X + x, paPedestrian.CellIndex.Y + y) then begin //ak je priechodna tak pre nu pocitam profit
            gain := paPedestrian.Matrix[paPedestrian.CellIndex.X, paPedestrian.CellIndex.y] - paPedestrian.Matrix[paPedestrian.CellIndex.X + x, paPedestrian.CellIndex.y + y];
            //gain := getGain(paWorld, tLogicalEntity(paPedestrian.Target), paPedestrian.CellIndex, x, y); // da zisk z posunutia x,y vzhladom k cielu
            pedCount := GetCountOfPedestrians(paWorld,paPedestrian.CellIndex.X + x, paPedestrian.CellIndex.Y + y, 1); // daj pocet chodcov v okoli tejto bunky
            barrierCount := GetCountOfBarrierCells(paWorld,paPedestrian.CellIndex.X + x, paPedestrian.CellIndex.Y + y, 1); // daj pocet stien v okoli tejto bunky

            /////////////////////////////////////////////////////////////////////
            ///  toto je ucelovka, podla ktorej vybera najvyhodnejsiu bunku   ///
            /////////////////////////////////////////////////////////////////////
            profit := (pedCount * CrowdTightning) +  (barrierCount * BarrierTightning)  + (gain * GainTightning);

            if profit > best  then begin //ulozim profit aj poziciu ak je lepsi ako doteraz najdeny
              resultX := x;
              resultY := y;
              best := profit;
              resGain := gain;
            end;
          end;
        end;

      end;
    end;

    if (best <> - MaxInt) then begin //hybem s pandrlakom len ak sa ma kam pohnut
      //zistujem stred bunky, na ktoru sa mam pohnut, hybem sa v spojitom svete
      destPos := TCoordinates.Create;
      destPos :=  Svet.CellWorld.getCentreOfCell(paPedestrian.CellIndex.X + resultX, paPedestrian.CellIndex.Y + resultY);
      Svet.updatePedestrian(paPedestrian, destPos);
      destPos.Free;
      if IsDestinationReached(tSink(paPedestrian.Target), paWorld.Cell[paPedestrian.CellIndex.X,paPedestrian.CellIndex.Y]) then
        result := true;
    end
  end;
  Docasny.Grid.Repaint;
end;

procedure PrepareWorld(paWorld : tLevel; paState : word);
  var pomPede : TPedestrian;
      i,j,k : integer;
      list : tList;
      polygon : tPolygon;
begin
  case paState of
    1 : begin
      //najprv physical layer
      insertBarrier(paWorld,1,40,165,160,165,40,175,160,175);
      insertBarrier(paWorld,2,180,165,290,165,180,175,290,175);

      // teraz sink
      list := tList.Create;
      list.Add(TCoordinates.Create(160 , 20));
      list.Add(TCoordinates.Create(180 , 20));
      list.Add(TCoordinates.Create(180 , 40));
      list.Add(TCoordinates.Create(160 , 40));
      polygon := Tpolygon.Create(list,100);
      paWorld.insertLogicalEntity(100,ET_SINK_TYPE,polygon,0);
    end;
    2 : begin
      insertBarrier(paWorld,1,15,41,45,41,15,41,45,41);
      insertBarrier(paWorld,2,45,0,45,0,45,40,45,40);
    end;
    3 : begin

    end;
  end;
  paWorld.getCellularWorld(10,10, 1);
  Stop := False;
end;


procedure insertBarrier(paWorld : tLevel; paID : integer; paLU_x, paLU_y, paRU_x, paRU_y, paLD_x, paLD_y, paRD_x, paRD_y: Real);
  var list : TList;
begin
  list := TList.Create;
  list.Add(TCoordinates.Create(paLD_x , paLD_y));
  list.Add(TCoordinates.Create(paLU_x , paLU_y));
  list.Add(TCoordinates.Create(paRU_x , paRU_y));
  list.Add(TCoordinates.Create(paRD_x , paRD_y ));
  paWorld.insertPhysicalEntity(Tpolygon.Create(list,paID));
end;


function IsRelevantCell(paLevel: TCellularWorld; paX,paY: integer):boolean;
begin
  if (Not paLevel.Cell[paX,paY].IsBarrier) AND (paLevel.Cell[paX,paY].isFree) then
    result := true
  else
    result := false;
end;


function getGain(paLevel : TCellularWorld; paDest : tLogicalEntity; paActPos : tPoint; paDX, paDY: integer) : integer;
    var newPos, TargetPos: tPoint;
    pom1X, pom2x, pomX, pom1Y, pom2Y, pomY : integer;
begin
  //kam mieri
  paLevel.detectCell(paDest.Position, TargetPos);

  //spravim si novu poziciu kvoli prehladnosti
  newPos.X := paActPos.X + paDX;
  newPos.Y := paActPos.Y + paDY;

  //najprv zistim Xovy zisk
  pom1X := abs(TargetPos.X - paActPos.X);
  pom2X := abs(TargetPos.X - newPos.X);
  if pom1X = pom2X then pomX := 0
  else if pom1X > pom2X then pomX := 1
       else pomX := -1;

  //potom Ynovy
  pom1Y := abs(TargetPos.Y - paActPos.Y);
  pom2Y := abs(TargetPos.Y - newPos.Y);
  if pom1Y = pom2Y then pomY := 0
  else if pom1Y > pom2Y then pomY := 1
       else pomY := -1;
  //nakoniec ich scitam
  result := pomX + pomY;
end;


function GetCountOfPedestrians(paWorld : TCellularWorld; paX,paY:integer; paDistance:integer):integer;
  var x,y : integer;
      count : integer;
begin
  count := 0;
  for x := (-1 * paDistance) to paDistance do
    for y := (-1 * paDistance) to paDistance do
      if IsIndexOK(paWorld , x + paX, y + paY) then
        count := count + paWorld.Cell[x + paX, y + paY].Pedestrians.Count;
  result := Count;
end;


function GetCountOfBarrierCells(paWorld : TCellularWorld; paX,paY:integer; paDistance:integer):integer;
  var x,y : integer;
      count : integer;
begin
  count := 0;
  for x := (-1 * paDistance) to paDistance do
    for y := (-1 * paDistance) to paDistance do
      if IsIndexOK(paWorld , x + paX, y + paY) then
        if paWorld.Cell[x + paX, y + paY].IsBarrier then
          inc(count);
  result := Count;
end;


end.
